#
# Copyright (c) 2023 supercell
#
# SPDX-License-Identifier: BSD-3-Clause
#

module Luce
  # Parses email-style blockquotes: `> quote`.
  class BlockquoteSyntax < BlockSyntax
    def pattern : Regex
      Luce.blockquote_pattern
    end

    # Whether this blockquote ends with a lazy continuation line
    # The definition of lazy continuation lines:
    # <https://spec.commonmark.org/0.30/#lazy-continuation-line>
    @@lazy_continuation = false

    def parse_child_lines(parser : BlockParser) : Array(Line)
      # Grab all of the lines that form the blockquote, stripping off the ">".
      child_lines = [] of Line
      @@lazy_continuation = false

      while !parser.done?
        current_line = parser.current
        match = pattern.match(parser.current.content)
        if !match.nil?
          # A block quote marker consists of a `>` together with an optional
          # following space of indentation, see
          # https://spec.commonmark.org/0.30/#block-quote-marker.
          marker_start = match[0].index('>') || 0
          marker_end = if current_line.content.size > 1
                         has_space = false
                         # Check if there is a following space if the marker is not at the end
                         # of this line.
                         if marker_start < current_line.content.size - 1
                           next_char = current_line.content.codepoint_at(marker_start + 1)
                           has_space = next_char == Charcode::TAB || next_char == Charcode::SPACE
                         end
                         marker_start + (has_space ? 2 : 1)
                       else
                         marker_start + 1
                       end
          child_lines << Line.new(current_line.content[marker_end..])
          parser.advance
          @@lazy_continuation = false
          next
        end

        last_line = child_lines.last

        # A paragraph continuation is OK. This is content that cannot
        # be parsed as any other syntax except Paragraph, and it
        # doesn't match the bar in a Setext header.
        # Because indented code blocks cannot interrupt paragraphs, a line
        # matched CodeBlockSyntax is also paragraph continuation text.
        other_matched = parser.block_syntaxes.find(&.can_parse?(parser))
        if (other_matched.is_a? ParagraphSyntax && !last_line.is_blank_line? && !Luce.code_fence_pattern.matches?(last_line.content)) ||
           (other_matched.is_a? CodeBlockSyntax && !Luce.indent_pattern.matches?(last_line.content))
          child_lines << parser.current
          @@lazy_continuation = true
          parser.advance
        else
          break
        end
      end

      child_lines
    end

    def parse(parser : BlockParser) : Node
      child_lines = parse_child_lines(parser).compact_map { |e| e }

      # Recursively parse the contents of the blockquote.
      children = BlockParser.new(child_lines, parser.document).parse_lines(
        # The setext heading underline cannot be a lazy continuation line in a
        # block quote.
        # https://spec.commonmark.org/0.30/#example-93
        disable_setext_heading: @@lazy_continuation,
        parent_syntax: self
      )

      Element.new("blockquote", children)
    end
  end
end
